#include "../config.h"

#include <glibmm/i18n.h>

#include "application.h"
#include "options.h"
#include "command.h"
#include "mainwindow.h"
#include "favorite.h"
#include "search.h"
#include "sound.h"
#include "screensaver.h"
#include "pref/p_dialog.h"

#ifdef ENABLE_DBUS_SERVICE
# include "dbusservice.h"
#endif

namespace gpyp {

//========================
// Application
void Application::on_channel_selected() {
  signal_channel_selected_(get_selected_channel());
}

void Application::ypcollection_on_loaded() {
  RefNotify notify                  = mainwindow_->notify();
  bool      notified                = false;
  int       notify_sound_rank       = -1;
  const Glib::ustring *notify_sound = &*notify->notify_sound();
  double    notify_sound_volume     =  *notify->notify_sound_volume();
  int       ss_active               = -1;
  for(YPCollection::iterator ypiter = ypcollection_->begin();
      ypiter != ypcollection_->end(); ++ypiter) {
    for(YellowPage::iterator chiter = (*ypiter)->begin();
        chiter != (*ypiter)->end(); ++chiter) {
      RefChannel ch = *chiter;
      RefFavoriteMatch fm = ch->get_favorite_match();
      if (!ch->get_id() || ch->get_is_notified() || fm->ng()) continue;
      ch->set_is_notified(true);
      if (ch->get_is_new()) {
        if (!ch->get_is_auto_played() && *auto_play_ && fm->auto_play()) {
          if (*conf_->screensaver()->disable_auto_play() &&
              (ss_active < 0 ? (ss_active = ss::get_active()) : ss_active)) {
            log::log << "SS: Autoplay ignored" << log::endl;
          } else {
            commandrunner_->auto_play(ch);
          }
        }
        if (fm->auto_record()) commandrunner_->auto_record(ch);
      }
      if (((ch->get_is_new() || ch->get_is_returned()) && fm->notify_new()) ||
          (ch->get_is_changed() && fm->notify_change())) {
        notified = true;
        if (ch->get_is_new()) {
          log::log << "New: " << ch->get_name() << log::endl;
          notify->notify_new(ch);
        } else if (ch->get_is_returned()) {
          log::log << "Returned: " << ch->get_name() << log::endl;
          notify->notify_new(ch);
        } else {
          log::log << "Change: " << ch->get_name() << log::endl;
          notify->notify_change(ch);
        }
        if (fm->notify_sound_rank() > notify_sound_rank) {
          notify_sound_rank   =  fm->notify_sound_rank();
          notify_sound        = &fm->notify_sound();
          notify_sound_volume =  fm->notify_sound_volume();
        }
      }
    }
  }
  if (notified) {
    notify->show();
    sound::play(*notify_sound, notify_sound_volume);
  }
}
void Application::ypcollection_on_error(YellowPage &yp) {
  mainwindow_->notify()->notify_error("<b>%s</b> %s", "%s %s",
                                      (*yp.name()).c_str(), yp.get_error_message().c_str());
}

void Application::commandrunner_process_on_aborted(RefProcess proc) {
  mainwindow_->notify()->notify_error(_("<b>%s</b> aborted"), _("%s aborted"), proc->get_name().c_str());
}

void Application::menu_yp_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("_Reload"), "@{YP.reload}", Gtk::Stock::REFRESH);
  menu.add_item(_("View _information"), "@{YP.viewinfo}", Gtk::Stock::PROPERTIES);
  menu.add_item(_("_Open with web browser"), "@{webbrowser} \"#{YP.url}\"", IconManager::SPECIAL_WEBBROWSER);
  menu.add_item(_("_Uptest"), "@{uptest}", IconManager::UPTEST);
}
void Application::menu_ch_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("_Play"), "@{play}", Gtk::Stock::MEDIA_PLAY);
  menu.add_special(_("Play _with ..."), ConfMenu::SPECIAL_TYPE_PLAYWITH, GTK_STOCK_MEDIA_PLAY);
  menu.add_item(_("_Record"), "@{record}", Gtk::Stock::MEDIA_RECORD);
  ConfMenu::RefMenu mi_ch_pc(menu.add_item(_("P_eerCast"), "", IconManager::PEERCAST));
  mi_ch_pc->add_item(_("_Information"), "@{webbrowser} \"http://#{Network.peercastsrv}/html/ja/relayinfo.html?id=#{Channel.id}\"", IconManager::SPECIAL_WEBBROWSER);
  mi_ch_pc->add_item(_("Relay _tree"), "@{webbrowser} \"http://#{Network.peercastsrv}/html/ja/tree.html?id=#{Channel.id}\"", IconManager::SPECIAL_WEBBROWSER);
  mi_ch_pc->add_item(_("_Bump"), "@{request_url} \"http://#{Network.peercastsrv}/admin?cmd=bump&id=#{Channel.id}\"");
  mi_ch_pc->add_item(_("_Disconnect"), "@{request_url} \"http://#{Network.peercastsrv}/admin?cmd=stop&id=#{Channel.id}\"", Gtk::Stock::STOP);
  menu.add_separator();
  ConfMenu::RefMenu mi_ch_open(menu.add_item(_("_Open")));
  mi_ch_open->add_item(_("_Open contact"), "@{webbrowser} \"#{Channel.contact}\"", IconManager::SPECIAL_WEBBROWSER);
  mi_ch_open->add_item(_("Open contact with _2ch browser"), "@{bbsbrowser} \"#{Channel.contact}\"", IconManager::SPECIAL_BBSBROWSER);
  mi_ch_open->add_item(_("Open _yp"), "@{webbrowser} \"#{YP.url}\"", IconManager::SPECIAL_WEBBROWSER);
  mi_ch_open->add_item(_("Open _chat"), "@{webbrowser} \"#{YP.url}chat.php?cn=#{Channel.name_url}\"", IconManager::SPECIAL_WEBBROWSER);
  ConfMenu::RefMenu mi_ch_copy(menu.add_item(_("Copy ..."), Gtk::Stock::COPY));
  mi_ch_copy->add_item(_("Copy _stream URL"), "@{copy} \"#{Channel.streamurl}\"", Gtk::Stock::COPY);
  mi_ch_copy->add_item(_("Copy play_list URL"), "@{copy} \"http://#{Network.peercastsrv}/pls/#{Channel.id}.pls?tip=#{Channel.tip}\"", Gtk::Stock::COPY);
  mi_ch_copy->add_item(_("Copy _contact"), "@{copy} \"#{Channel.contact}\"", Gtk::Stock::COPY);
  mi_ch_copy->add_item(_("Copy _name"), "@{copy} \"#{Channel.name}\"", Gtk::Stock::COPY);
  menu.add_item(_("_Reload yp"), "@{Channel.reloadyp}", Gtk::Stock::REFRESH);
  {
    const char *c =
      "@{Favorite.Rule.append_word} --favorite-name \"%s\" \"#{Channel.name}\";"
      "@{Channel.rematch_favorite};@{Channellist.resort};@{Channellist.refilter}";
    menu.add_item(_("Add to _favorite"), format(c, _("Favorite")), IconManager::FAVORITE);
    menu.add_item(_("Add to _NG"), format(c, _("NG")));
  }
  menu.add_separator();
  menu.add_item(_("View _information"), "@{Channel.viewinfo}", Gtk::Stock::PROPERTIES);
}
void Application::menu_tool_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("Run _peercast"), "@{peercast}", IconManager::PEERCAST);
  menu.add_item(_("Run pc_raw__proxy"), "@{pcraw_proxy}");
  menu.add_item(_("_Uptest"), "@{uptest}", IconManager::UPTEST);
}
void Application::menu_text_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("Open with _web browser"), "@{webbrowser} \"#{text}\"", IconManager::SPECIAL_WEBBROWSER);
  menu.add_item(_("Open with _2ch browser"), "@{bbsbrowser} \"#{text}\"", IconManager::SPECIAL_BBSBROWSER);
  menu.add_item(_("_Google"), "@{webbrowser} \"http://www.google.co.jp/search?q=#{textu}\"", IconManager::GOOGLE);
}
void Application::menu_proc_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("_Terminate"), "@{Process.terminate}");
  menu.add_item(_("_Kill"), "@{Process.kill}");
}
void Application::menu_pc_relay_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("_Bump"),       "@{PeerCast.Relay.bump}");
  menu.add_item(_("_Disconnect"), "@{PeerCast.Relay.disconnect}");
  menu.add_item(_("_Keep"),       "@{PeerCast.Relay.keep}");
  menu.add_separator();
  menu.add_item(_("_Play"), "@{play}", Gtk::Stock::MEDIA_PLAY);
  menu.add_special(_("Play _with ..."), ConfMenu::SPECIAL_TYPE_PLAYWITH, GTK_STOCK_MEDIA_PLAY);
  menu.add_item(_("_Record"), "@{record}", Gtk::Stock::MEDIA_RECORD);
  menu.add_separator();
  menu.add_item(_("_Information"), "@{webbrowser} \"http://#{Network.peercastsrv}/html/ja/relayinfo.html?id=#{Channel.id}\"", IconManager::SPECIAL_WEBBROWSER);
  menu.add_item(_("Relay _tree"), "@{webbrowser} \"http://#{Network.peercastsrv}/html/ja/tree.html?id=#{Channel.id}\"", IconManager::SPECIAL_WEBBROWSER);
  menu.add_separator();
  menu.add_item(_("Deactivate _PeerCast"), "@{PeerCast.deactivate}", Gtk::Stock::STOP);
}
void Application::menu_pc_connection_on_load_default(ConfMenu::Menu &menu) {
  menu.add_item(_("_Disconnect"), "@{PeerCast.Connection.disconnect}");
  menu.add_separator();
  menu.add_item(_("Deactivate _PeerCast"), "@{PeerCast.deactivate}", Gtk::Stock::STOP);
}
void Application::menu_webview_on_load_default(ConfMenu::Menu &/*menu*/) {
}

void Application::key_mainwindow_on_load_default(KeyBind &keybind) {
  keybind.add_item('r', Gdk::CONTROL_MASK, "@{YP.reload_all}");
  keybind.add_item('p', Gdk::CONTROL_MASK, "@{Pref.show}");
  keybind.add_item('f', Gdk::CONTROL_MASK | Gdk::MOD1_MASK, "@{Filterbar.toggle}");
  keybind.add_item('f', Gdk::CONTROL_MASK, "@{Searchbar.show}");
  keybind.add_item('q', Gdk::CONTROL_MASK, "@{quit}");
}
void Application::key_channellist_on_load_default(KeyBind &keybind) {
  keybind.add_item(GDK_Return, (Gdk::ModifierType)0, "@{play}");
  keybind.add_item(GDK_Escape, (Gdk::ModifierType)0, "@{Searchbar.hide}");
  keybind.add_item('o', Gdk::CONTROL_MASK, "@{webbrowser} \"#{Channel.contact}\"");
  keybind.add_item('o', Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, "@{bbsbrowser} \"#{Channel.contact}\"");
  keybind.add_item('c', Gdk::CONTROL_MASK, "@{copy} \"#{Channel.streamurl}\"");
  keybind.add_item('c', Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, "@{copy} \"#{Channel.contact}\"");
}
void Application::key_text_editor_on_load_default(KeyBind &keybind) {
  keybind.add_item('f', Gdk::CONTROL_MASK, "@{Searchbar.show}");
}
void Application::key_search_on_load_default(KeyBind &keybind) {
  keybind.add_item(GDK_Escape, (Gdk::ModifierType)0, "@{Searchbar.hide}");
  keybind.add_item('g', Gdk::CONTROL_MASK, "@{Searchbar.clear}");
}
void Application::key_searchbar_on_load_default(KeyBind &keybind) {
  keybind.add_item(GDK_Return, (Gdk::ModifierType)0, "@{play};@{Searchbar.hide}");
}
void Application::key_webview_on_load_default(KeyBind &keybind) {
  keybind.add_item(GDK_F5, (Gdk::ModifierType)0, "@{WebView.reload}");
  keybind.add_item(GDK_Escape, (Gdk::ModifierType)0, "@{WebView.stop}");
  keybind.add_item(GDK_BackSpace, (Gdk::ModifierType)0, "@{WebView.back}");
  keybind.add_item('f', Gdk::CONTROL_MASK, "@{WebView.search}");
}

int Application::main(Gtk::Main &kit) {
#if GTK_CHECK_VERSION(2, 12, 0)
  Gtk::Window::set_default_icon_name(IconManager::GPYP);
#else
  Gtk::Window::set_default_icon(IconManager::load_icon(IconManager::GPYP, IconManager::ICON_SIZE_16));
#endif

  thread_pool_ = new Glib::ThreadPool();

#ifdef ENABLE_DBUS_SERVICE
  dbus::init(*this);
#endif

  sound::init(*this);
  ss::init();
  conf_->load(opts_.get_use_config());
  loader::Pool::init(*this);
  Search::init(*this);

  ypcollection_->load_cache();
  ypcollection_->signal_loaded()
    .connect(sigc::mem_fun(*this, &Application::ypcollection_on_loaded));
  ypcollection_->signal_error()
    .connect(sigc::mem_fun(*this, &Application::ypcollection_on_error));
  if (*conf_->general()->run_peercast())    commandrunner_->run_peercast(true);
  if (*conf_->general()->run_pcraw_proxy()) commandrunner_->run_pcraw_proxy(true);
  //kit.run(*mainwindow_);
  kit.run();

  Search::uninit();
  loader::Pool::uninit();
  if (opts_.get_use_config()) conf_->save();
  ss::uninit();
  sound::stop_all();

#ifdef ENABLE_DBUS_SERVICE
  dbus::uninit();
#endif

  thread_pool_->shutdown();
  delete thread_pool_;
  sound::uninit();

  ypcollection_ .reset();
  favcollection_.reset();
  mainwindow_   .reset();
  return 0;
}

RefChannel Application::get_selected_channel() {
  return mainwindow_->channellist()->get_selected_channel();
}

bool Application::bc_config_save(ShellLine &/*line*/, CommandOpts &/*opts*/) {
  conf_->save();
  return true;
}
bool Application::bc_config_view(ShellLine &/*line*/, CommandOpts &/*opts*/) {
  mainwindow_->show_config_view();
  return true;
}
bool Application::bc_debug_g_mem_profile(ShellLine &/*line*/, CommandOpts &/*opts*/) {
  g_mem_profile();
  return true;
}

Application::Application(OptionContext &opts) :
  auto_play_    ("auto_play", true),
  opts_         (opts),
  bc_root_      (new BuiltinCommand()),
  conf_         (new Conf(opts_.get_config_dir())),
  iconmanager_  (*this),
  commandrunner_(new CommandRunner(*this, *bc_root_)),
  ypcollection_ (new YPCollection(*this)),
  favcollection_(new FavoriteCollection(*this)),
  mainwindow_   (new MainWindow(*this)),
  thread_pool_  (NULL),
  bc_config_    (*bc_root_->add_ns("Config")),
  bc_debug_     (*bc_root_->add_ns("Debug"))
{
#define CONNECT_MENU_LOAD_DEFAULT(NAME)                                 \
  conf_->menu()->NAME()->signal_load_default()                          \
    .connect(sigc::mem_fun(*this, &Application::menu_##NAME##_on_load_default));
#define CONNECT_KEY_LOAD_DEFAULT(N)                                     \
  conf_->input()->N()->key()->signal_load_default()                     \
    .connect(sigc::mem_fun(*this, &Application::key_##N##_on_load_default));

  commandrunner_->signal_aborted()
    .connect(sigc::mem_fun(*this, &Application::commandrunner_process_on_aborted));

  CONNECT_MENU_LOAD_DEFAULT(yp);
  CONNECT_MENU_LOAD_DEFAULT(ch);
  CONNECT_MENU_LOAD_DEFAULT(tool);
  CONNECT_MENU_LOAD_DEFAULT(text);
  CONNECT_MENU_LOAD_DEFAULT(proc);
  CONNECT_MENU_LOAD_DEFAULT(pc_relay);
  CONNECT_MENU_LOAD_DEFAULT(pc_connection);
  CONNECT_MENU_LOAD_DEFAULT(webview);

  CONNECT_KEY_LOAD_DEFAULT(mainwindow);
  CONNECT_KEY_LOAD_DEFAULT(search);
  CONNECT_KEY_LOAD_DEFAULT(searchbar);
  CONNECT_KEY_LOAD_DEFAULT(channellist);
  CONNECT_KEY_LOAD_DEFAULT(text_editor);
  CONNECT_KEY_LOAD_DEFAULT(webview);

  RefConfMain main = conf_->main();
  main->add(ypcollection_);
  main->add(mainwindow_);
  conf_->favorite()->add(favcollection_);

  mainwindow_->channellist()->get_selection()->signal_changed()
    .connect(sigc::mem_fun(*this, &Application::on_channel_selected));

#define BC_ADD_CMD(NS, CMD, H)                                          \
  bc_##NS##_.add_cmd(#CMD, sigc::mem_fun(*this, &Application::bc_##NS##_##CMD), H)
  BC_ADD_CMD(config, save, _("Save configuration files"));
  BC_ADD_CMD(config, view, _("Run config viewer"));
  BC_ADD_CMD(debug,  g_mem_profile, "g_mem_profile()");
}
Application::~Application() {
  delete commandrunner_;
  delete conf_;
  delete bc_root_;
}

}/*gpyp*/
